package org.checkerframework.checker.guieffect;

import java.lang.annotation.Annotation;
import org.checkerframework.checker.guieffect.qual.PolyUIEffect;
import org.checkerframework.checker.guieffect.qual.SafeEffect;
import org.checkerframework.checker.guieffect.qual.UIEffect;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.checkerframework.dataflow.qual.Pure;
import org.checkerframework.dataflow.qual.SideEffectFree;

/** An effect -- either UIEffect, PolyUIEffect, or SafeEffect. */
public final class Effect {
  // Colin hates Java's comparable interface, so he's not using it

  private final Class<? extends Annotation> annotClass;

  /**
   * Create a new Effect object.
   *
   * @param cls one of UIEffect.class, PolyUIEffect.class, or SafeEffect.class
   */
  public Effect(Class<? extends Annotation> cls) {
    assert cls == UIEffect.class || cls == PolyUIEffect.class || cls == SafeEffect.class;
    annotClass = cls;
  }

  /**
   * Returns true iff {@code left} is less than or equal to {@code right}.
   *
   * @param left the first effect to compare
   * @param right the first effect to compare
   * @return true iff {@code left} is less than or equal to {@code right}
   */
  public static boolean lessThanOrEqualTo(Effect left, Effect right) {
    assert (left != null && right != null);
    boolean leftBottom = left.annotClass == SafeEffect.class;
    boolean rightTop = right.annotClass == UIEffect.class;
    return leftBottom || rightTop || left.annotClass == right.annotClass;
  }

  public static Effect min(Effect l, Effect r) {
    if (lessThanOrEqualTo(l, r)) {
      return l;
    } else {
      return r;
    }
  }

  public static final class EffectRange {
    public final Effect min;
    public final Effect max;

    public EffectRange(@Nullable Effect min, @Nullable Effect max) {
      assert (min != null || max != null);
      // If one is null, fill in with the other
      this.min = (min != null ? min : max);
      this.max = (max != null ? max : min);
    }
  }

  /**
   * Returns true if this is SafeEffect.
   *
   * @return true if this is SafeEffect
   */
  public boolean isSafe() {
    return annotClass == SafeEffect.class;
  }

  /**
   * Returns true if this is UIEffect.
   *
   * @return true if this is UIEffect
   */
  public boolean isUI() {
    return annotClass == UIEffect.class;
  }

  /**
   * Returns true if this is PolyUIEffect.
   *
   * @return true if this is PolyUIEffect
   */
  @Pure
  public boolean isPoly() {
    return annotClass == PolyUIEffect.class;
  }

  public Class<? extends Annotation> getAnnot() {
    return annotClass;
  }

  @SideEffectFree
  @Override
  public String toString() {
    return annotClass.getSimpleName();
  }

  /**
   * Returns true if this equals the given effect.
   *
   * @param e the effect to compare this to
   * @return true if this equals the given effect
   */
  @SuppressWarnings("NonOverridingEquals") // TODO: clean this up!
  public boolean equals(Effect e) {
    return annotClass == e.annotClass;
  }

  @Override
  @SuppressWarnings("SuperEqualsIsObjectEquals") // better style to use the equals() abstraction
  public boolean equals(@Nullable Object o) {
    if (o instanceof Effect) {
      return this.equals((Effect) o);
    } else {
      return super.equals(o);
    }
  }

  @Pure
  @Override
  public int hashCode() {
    return 31 + annotClass.hashCode();
  }
}
